package fields

import (
	"bytes"
	"fmt"
	"sort"
	"strings"
)

// Selector 是仅由 ==、=、!= 三种符号组合成的表达式字符串，
// 可以包括多个表达式，它们之间用逗号分隔，
// 等号和双等号都对应 hasTerm，
// 不等号对应 notHasTerm，
// 多个表达式对应 andTerm，
// 空字符串对应 Everything，
// 只可以对等号、斜杠、逗号加斜杆转义，
// 空格当普通字符处理，
type Selector interface {
	// 检查 Selector 中的 field 是否存在于 Fields（一个 map） 中，且值是否相等，
	// 注意把 andTerm 为空的时候表示 EveryThing，它能匹配所有 Fields，
	Matches(Fields) bool

	// 返回 Selector 是否不包含任何内容（可能是从空字符串转换过来的，或者是一个 nil 的 andTerm）
	Empty() bool

	// 检查 Selector 中是否包含一个等号表达式，表达式的左值和 field 一样，如果有，就返回表达式的值
	RequiresExactMatch(field string) (value string, found bool)

	// 提供一个过滤函数，可以用于去掉或者替换 Selector 中的某些表达式
	Transform(fn TransformFunc) (Selector, error)

	// 把表达式拆成三元组
	Requirements() Requirements

	// String returns a human readable string that represents this selector.
	String() string

	// Make a deep copy of the selector.
	DeepCopySelector() Selector
}

type nothingSelector struct{}

func (n nothingSelector) Matches(_ Fields) bool      { return false }
func (n nothingSelector) Empty() bool                { return false }
func (n nothingSelector) String() string             { return "" }
func (n nothingSelector) Requirements() Requirements { return nil }
func (n nothingSelector) DeepCopySelector() Selector { return n }
func (n nothingSelector) RequiresExactMatch(field string) (value string, found bool) {
	return "", false
}
func (n nothingSelector) Transform(fn TransformFunc) (Selector, error) { return n, nil }

// Nothing returns a selector that matches no fields.
func Nothing() Selector {
	return nothingSelector{}
}

// Everything returns a selector that matches all fields.
func Everything() Selector {
	return andTerm{}
}

type hasTerm struct {
	field, value string
}

func (t *hasTerm) Matches(ls Fields) bool {
	return ls.Get(t.field) == t.value
}

func (t *hasTerm) Empty() bool {
	return false
}

func (t *hasTerm) RequiresExactMatch(field string) (value string, found bool) {
	if t.field == field {
		return t.value, true
	}

	return "", false
}

func (t *hasTerm) Transform(fn TransformFunc) (Selector, error) {
	field, value, err := fn(t.field, t.value)
	if err != nil {
		return nil, err
	}
	if len(field) == 0 && len(value) == 0 {
		return Everything(), nil
	}

	return &hasTerm{field, value}, nil
}

func (t *hasTerm) Requirements() Requirements {
	return []Requirement{{
		Field:    t.field,
		Operator: Equals,
		Value:    t.value,
	}}
}

func (t *hasTerm) String() string {
	return fmt.Sprintf("%v=%v", t.field, EscapeValue(t.value))
}

func (t *hasTerm) DeepCopySelector() Selector {
	if t == nil {
		return nil
	}
	out := new(hasTerm)
	*out = *t

	return out
}

type notHasTerm struct {
	field, value string
}

func (t *notHasTerm) Matches(ls Fields) bool {
	return ls.Get(t.field) != t.value
}

func (t *notHasTerm) Empty() bool {
	return false
}

func (t *notHasTerm) RequiresExactMatch(field string) (value string, found bool) {
	return "", false
}

func (t *notHasTerm) Transform(fn TransformFunc) (Selector, error) {
	field, value, err := fn(t.field, t.value)
	if err != nil {
		return nil, err
	}
	if len(field) == 0 && len(value) == 0 {
		return Everything(), nil
	}

	return &notHasTerm{field, value}, nil
}

func (t *notHasTerm) Requirements() Requirements {
	return []Requirement{{
		Field:    t.field,
		Operator: NotEquals,
		Value:    t.value,
	}}
}

func (t *notHasTerm) String() string {
	return fmt.Sprintf("%v!=%v", t.field, EscapeValue(t.value))
}

func (t *notHasTerm) DeepCopySelector() Selector {
	if t == nil {
		return nil
	}
	out := new(notHasTerm)
	*out = *t

	return out
}

type andTerm []Selector

func (t andTerm) Matches(ls Fields) bool {
	for _, q := range t {
		if !q.Matches(ls) {
			return false
		}
	}

	return true
}

func (t andTerm) Empty() bool {
	if t == nil {
		return true
	}
	if len([]Selector(t)) == 0 {
		return true
	}
	for i := range t {
		if !t[i].Empty() {
			return false
		}
	}

	return true
}

func (t andTerm) RequiresExactMatch(field string) (string, bool) {
	if t == nil || len([]Selector(t)) == 0 {
		return "", false
	}
	for i := range t {
		if value, found := t[i].RequiresExactMatch(field); found {
			return value, found
		}
	}

	return "", false
}

func (t andTerm) Transform(fn TransformFunc) (Selector, error) {
	next := make([]Selector, 0, len([]Selector(t)))
	for _, s := range []Selector(t) {
		n, err := s.Transform(fn)
		if err != nil {
			return nil, err
		}
		if !n.Empty() {
			next = append(next, n)
		}
	}

	return andTerm(next), nil
}

func (t andTerm) Requirements() Requirements {
	reqs := make([]Requirement, 0, len(t))
	for _, s := range []Selector(t) {
		rs := s.Requirements()
		reqs = append(reqs, rs...)
	}

	return reqs
}

func (t andTerm) String() string {
	terms := make([]string, 0, len(t))
	for _, q := range t {
		terms = append(terms, q.String())
	}

	return strings.Join(terms, ",")
}

func (t andTerm) DeepCopySelector() Selector {
	if t == nil {
		return nil
	}
	out := make([]Selector, 0, len(t))
	for i := range t {
		out[i] = t[i].DeepCopySelector()
	}

	return andTerm(out)
}

// 把一个字典结构解析成 Selector
func SelectorFromSet(ls Set) Selector {
	if ls == nil {
		return Everything()
	}
	items := make([]Selector, 0, len(ls))
	for field, value := range ls {
		items = append(items, &hasTerm{field: field, value: value})
	}
	if len(items) == 1 {
		return items[0]
	}

	return andTerm(items)
}

// 用于对转义斜杠、等号、逗号添加转义斜杠，
// 注意要先给斜杠添加转义，再处理逗号和等号
func EscapeValue(s string) string {
	return strings.NewReplacer(
		`\`, `\\`,
		`,`, `\,`,
		`=`, `\=`,
	).Replace(s)
}

// 自定义的错误类型
type InvalidEscapeSequence struct {
	sequence string
}

func (i InvalidEscapeSequence) Error() string {
	return fmt.Sprintf("invalid field selector: invalid escape sequence: %s", i.sequence)
}

// 自定义的错误类型
type UnescapedRune struct {
	r rune
}

func (i UnescapedRune) Error() string {
	return fmt.Sprintf("invalid field selector: unescaped character in value: %v", i.r)
}

// 把字符串中被转义的斜杠、逗号、等号，进行反转义，去掉前面的斜杠，
// 字符串中没有斜杠、逗号、等号的不用处理，
// 如果字符串中有逗号、等号，但发现没有被转义，就返回错误，
// 或者其他字符前加了斜杠，也返回错误
func UnescapeValue(s string) (string, error) {
	// if there's no escaping or special characters, just return to avoid allocation
	if !strings.ContainsAny(s, `\,=`) {
		return s, nil
	}

	v := bytes.NewBuffer(make([]byte, 0, len(s)))
	inSlash := false
	for _, c := range s {
		if inSlash {
			switch c {
			case '\\', ',', '=':
				// 被转义的斜杠、逗号、等号，去掉前面的转义斜杠
				v.WriteRune(c)
			default:
				// 发现在其它字符前加了斜杠，返回错误
				return "", InvalidEscapeSequence{sequence: string([]rune{'\\', c})}
			}
			inSlash = false
			continue
		}

		switch c {
		case '\\':
			inSlash = true
		case ',', '=':
			// 发现逗号、等号没有被转义，返回错误
			return "", UnescapedRune{r: c}
		default:
			v.WriteRune(c)
		}
	}

	// Ending with a single backslash is an invalid sequence
	if inSlash {
		return "", InvalidEscapeSequence{sequence: "\\"}
	}

	return v.String(), nil
}

// 把一个字符串解析成 Selector
func ParseSelectorOrDie(s string) Selector {
	selector, err := ParseSelector(s)
	if err != nil {
		panic(err)
	}

	return selector
}

// 把一个字符串解析成 Selector
func ParseSelector(selector string) (Selector, error) {
	return parseSelector(selector,
		func(lhs, rhs string) (newLhs, newRhs string, err error) {
			return lhs, rhs, nil
		})
}

// 把一个字符串解析成 Selector，同时提供转换函数
func ParseAndTransformSelector(selector string, fn TransformFunc) (Selector, error) {
	return parseSelector(selector, fn)
}

// 转换函数定义
type TransformFunc func(field, value string) (newField, newValue string, err error)

// 根据逗号来切割字符串，逗号前加了转义斜杠的不算，有几个逗号就切成几段
func splitTerms(fieldSelector string) []string {
	if len(fieldSelector) == 0 {
		return nil
	}

	terms := make([]string, 0, 1)
	startIndex := 0
	inSlash := false
	for i, c := range fieldSelector {
		switch {
		case inSlash:
			inSlash = false
		case c == '\\':
			inSlash = true
		case c == ',':
			terms = append(terms, fieldSelector[startIndex:i])
			startIndex = i + 1
		}
	}

	terms = append(terms, fieldSelector[startIndex:])

	return terms
}

const (
	notEqualOperator    = "!="
	doubleEqualOperator = "=="
	equalOperator       = "="
)

var termOperators = []string{notEqualOperator, doubleEqualOperator, equalOperator}

// 仅仅根据 ==、!、= 来切割字符串，
// 双等号和单等号在这里用途是一样的，都对应 hasTerm，注意要先匹配双等号
func splitTerm(term string) (lhs, op, rhs string, ok bool) {
	for i := range term {
		remaining := term[i:]
		for _, op := range termOperators {
			if strings.HasPrefix(remaining, op) {
				return term[0:i], op, term[i+len(op):], true
			}
		}
	}

	return "", "", "", false
}

// selector 为空字符串时，会解析成空的 andTerm，也就是 Everything，它可以 Matches 所有 selector。
func parseSelector(selector string, fn TransformFunc) (Selector, error) {
	// 根据逗号切割成表达式
	parts := splitTerms(selector)
	sort.StringSlice(parts).Sort()
	var items []Selector
	for _, part := range parts {
		if part == "" {
			continue
		}
		// 根据双等号、等号、不等号切割成元素
		lhs, op, rhs, ok := splitTerm(part)
		if !ok {
			return nil, fmt.Errorf("invalid selector: '%s'; can't understand '%s'", selector, part)
		}
		// 把表达式右边的值去掉转义
		unescapedRHS, err := UnescapeValue(rhs)
		if err != nil {
			return nil, err
		}
		switch op {
		case notEqualOperator:
			items = append(items, &notHasTerm{field: lhs, value: unescapedRHS})
		case doubleEqualOperator:
			items = append(items, &hasTerm{field: lhs, value: unescapedRHS})
		case equalOperator:
			items = append(items, &hasTerm{field: lhs, value: unescapedRHS})
		default:
			return nil, fmt.Errorf("invalid selector: '%s'; can't understand '%s'", selector, part)
		}
	}
	if len(items) == 1 {
		return items[0].Transform(fn)
	}

	return andTerm(items).Transform(fn)
}

// 把一对字符组装成 hasTerm
func OneTermEqualSelector(k, v string) Selector {
	return &hasTerm{field: k, value: v}
}

// 把一对字符组装成 notHasTerm
func OneTermNotEqualSelector(k, v string) Selector {
	return &notHasTerm{field: k, value: v}
}

// 把一串 Selector 组装成 andTerm
func AndSelectors(selectors ...Selector) Selector {
	return andTerm(selectors)
}
